package com.liang.subject.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.liang.subject.domain.convert.SubjectCategoryConverter;
import com.liang.subject.domain.convert.SubjectLabelConverter;
import com.liang.subject.domain.entity.SubjectCategoryBO;
import com.liang.subject.domain.entity.SubjectLabelBO;
import com.liang.subject.domain.service.SubjectCategoryDomainService;
import com.liang.subject.domain.utils.CacheUtils;
import com.liang.subject.infra.basic.entity.SubjectCategory;
import com.liang.subject.infra.basic.entity.SubjectLabel;
import com.liang.subject.infra.basic.service.SubjectCategoryService;
import com.liang.subject.infra.basic.service.SubjectMappingService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {

    private final SubjectCategoryService subjectCategoryService;
    private final SubjectMappingService subjectMappingService;
    private final ThreadPoolExecutor labelThreadPool;
    private final CacheUtils<SubjectCategoryBO> localCache;


    @Override
    public void save(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryController.save.bo：{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategoryService.save(subjectCategory);
    }

    @Override
    public List<SubjectCategoryBO> queryCategory(SubjectCategoryBO subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);
        List<SubjectCategoryBO> categoryBOList = SubjectCategoryConverter.INSTANCE.toCategoryBOList(subjectCategoryList);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryController.queryPrimaryCategory：{}", JSON.toJSONString(categoryBOList));
        }
        categoryBOList.forEach(bo -> {
            Integer count = subjectCategoryService.getSubjectCount(bo.getId());
            bo.setCount(count);
        });
        return categoryBOList;
    }

    @Override
    public List<SubjectCategoryBO> queryCategoryByPrimary(SubjectCategoryBO subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategoryByPrimary(subjectCategory);
        List<SubjectCategoryBO> categoryBOList = SubjectCategoryConverter.INSTANCE.toCategoryBOList(subjectCategoryList);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryController.queryCategoryByPrimary：{}", JSON.toJSONString(categoryBOList));
        }
        return categoryBOList;
    }

    @Override
    public Boolean updateCategory(SubjectCategoryBO subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        boolean b = subjectCategoryService.updateById(subjectCategory);
        return b;
    }

    @Override
    public Boolean delete(SubjectCategoryBO subjectCategoryBO) {
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        return subjectCategoryService.removeById(subjectCategory);
    }

    @Override
    @SneakyThrows
    public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {
        Long id = subjectCategoryBO.getId();
        String cacheKey = "categoryAndLabel." + id;
        return localCache.getContent(cacheKey, SubjectCategoryBO.class, key -> getSubjectCategoryBOS(id));
    }

    private List<SubjectCategoryBO> getSubjectCategoryBOS(Long id) {
        List<SubjectCategory> categoryList = subjectCategoryService.queryAllCategories(id);
        List<SubjectCategoryBO> categoryBOList = SubjectCategoryConverter.INSTANCE.toCategoryBOList(categoryList);

        List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = categoryBOList.stream()
                .map(categoryBO -> {
                    return CompletableFuture.supplyAsync(() -> getLabelBoList(categoryBO), labelThreadPool);
                }).collect(Collectors.toList());
        Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
        completableFutureList.forEach(future -> {
            try {
                Map<Long, List<SubjectLabelBO>> resMap = future.get();
                map.putAll(resMap);
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
        categoryBOList.forEach(category -> {
            category.setLabelBOList(map.get(category.getId()));
        });
        return categoryBOList;
    }

    private Map<Long, List<SubjectLabelBO>> getLabelBoList(SubjectCategoryBO category) {
        Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
        List<SubjectLabel> labelList = subjectMappingService.getLabelListByCategoryId(category.getId());
        List<SubjectLabelBO> boList = SubjectLabelConverter.INSTANCE.convertToBoList(labelList);
        map.put(category.getId(), boList);
        return map;
    }
}
